#!/usr/bin/env python3
"""
驱逐舰态势对象数据导入脚本
从 Excel 文件读取驱逐舰数据并导入到 PostgreSQL 数据库
"""

import sys
import os
import logging
import asyncio
import json
import re
from pathlib import Path
from datetime import datetime
from typing import Dict, Any, List

# 尝试导入 pandas 和 openpyxl
try:
    import pandas as pd
    HAS_PANDAS = True
except ImportError:
    HAS_PANDAS = False
    logging.warning("pandas 未安装，将尝试使用其他方式读取Excel文件")

try:
    import openpyxl
    HAS_OPENPYXL = True
except ImportError:
    HAS_OPENPYXL = False
    logging.warning("openpyxl 未安装，将尝试使用其他方式读取Excel文件")

# 添加项目根目录到 Python 路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

# 直接导入所需模块，避免触发 src.__init__ 中的图数据库初始化
import src.situation.database
import src.situation.models
import src.situation.service
import src.situation.schemas

from src.situation.database import get_db_session, engine, Base
from src.situation.models import SituationObject
from src.situation.service import SituationService
from src.situation.schemas import SituationObjectCreate, DeploymentStatus, ObjectType


# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('logs/destroyer_import.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


def read_excel_file(file_path: str) -> List[Dict[str, Any]]:
    """
    读取 Excel 文件并返回数据列表

    Args:
        file_path: Excel 文件路径

    Returns:
        包含驱逐舰数据的字典列表
    """
    try:
        if HAS_PANDAS and HAS_OPENPYXL:
            # 使用 pandas 读取 Excel 文件
            df = pd.read_excel(file_path)
            logger.info(f"使用 pandas 成功读取 Excel 文件，共 {len(df)} 行数据")

            # 转换为字典列表
            data = df.to_dict('records')

            # 记录列名
            logger.info(f"Excel 文件包含的列: {list(df.columns)}")

            return data
        else:
            # 使用 openpyxl 直接读取
            if not HAS_OPENPYXL:
                raise ImportError("需要安装 pandas 和 openpyxl 来读取 Excel 文件")

            from openpyxl import load_workbook

            wb = load_workbook(filename=file_path, read_only=True)
            sheet = wb.active

            # 读取标题行
            headers = [cell.value for cell in next(sheet.iter_rows(min_row=1, max_row=1))]
            logger.info(f"Excel 文件包含的列: {headers}")

            # 读取数据行
            data = []
            for row in sheet.iter_rows(min_row=2, values_only=True):
                if any(row):  # 跳过空行
                    row_dict = {headers[i]: row[i] for i in range(len(headers)) if i < len(row)}
                    data.append(row_dict)

            wb.close()
            logger.info(f"使用 openpyxl 成功读取 Excel 文件，共 {len(data)} 行数据")
            return data

    except Exception as e:
        logger.error(f"读取 Excel 文件失败: {e}")
        if not HAS_PANDAS or not HAS_OPENPYXL:
            logger.error("请安装必要的依赖: uv add pandas openpyxl")
        raise


def parse_float_field(field_value: Any) -> float:
    """
    解析浮点数字段，支持复杂格式

    Args:
        field_value: 字段值

    Returns:
        浮点数或 None
    """
    if field_value is None or field_value == '':
        return None

    try:
        if isinstance(field_value, (int, float)):
            return float(field_value)

        if isinstance(field_value, str):
            # 移除逗号分隔符
            cleaned = field_value.replace(',', '').replace('，', '')

            # 提取数字部分（包括小数点和负号）
            # 处理范围格式：8,300–9,900 吨
            match = re.search(r'([\d,]+\.?\d*)\s*[-–—~]\s*([\d,]+\.?\d*)', cleaned)
            if match:
                # 取平均值
                num1 = float(match.group(1).replace(',', ''))
                num2 = float(match.group(2).replace(',', ''))
                return (num1 + num2) / 2

            # 提取单个数字
            numbers = re.findall(r'-?\d+\.?\d*', cleaned)
            if numbers:
                return float(numbers[0])

            logger.warning(f"字符串中未找到有效数字: {field_value}")
            return None

    except (ValueError, TypeError) as e:
        logger.warning(f"无法解析浮点数: {field_value}, 错误: {e}")
        return None

    return None


def parse_year_field(field_value: Any) -> int:
    """
    解析年份字段

    Args:
        field_value: 字段值

    Returns:
        年份整数或 None
    """
    if field_value is None or field_value == '':
        return None

    try:
        if isinstance(field_value, int):
            return field_value

        if isinstance(field_value, str):
            # 提取4位数年份
            match = re.search(r'\b(19|20)\d{2}\b', field_value)
            if match:
                return int(match.group(0))

        logger.warning(f"字符串中未找到有效年份: {field_value}")
        return None

    except (ValueError, TypeError) as e:
        logger.warning(f"无法解析年份: {field_value}, 错误: {e}")
        return None

    return None


def parse_list_field(field_value: Any, separator: str = '、') -> List[str]:
    """
    解析列表字段

    Args:
        field_value: 字段值
        separator: 分隔符

    Returns:
        字符串列表
    """
    if field_value is None or field_value == '':
        return []

    if isinstance(field_value, list):
        return [str(item).strip() for item in field_value if item]

    if isinstance(field_value, str):
        # 尝试按分隔符分割
        separators = [separator, ',', '，', ';', '；', '|']
        for sep in separators:
            if sep in field_value:
                return [item.strip() for item in field_value.split(sep) if item.strip()]

        # 如果没有分隔符，作为单个元素
        return [field_value.strip()] if field_value.strip() else []

    return [str(field_value)]


def convert_to_situation_object(destroyer_data: Dict[str, Any]) -> Dict[str, Any]:
    """
    将驱逐舰数据转换为态势对象格式

    Args:
        destroyer_data: 驱逐舰原始数据

    Returns:
        符合态势对象模型的数据字典
    """
    try:
        # 基础信息映射
        name = destroyer_data.get('名称') or destroyer_data.get('name')

        # 从名称中提取英文部分作为编码
        code = None
        if name:
            # 提取括号中的英文部分
            match = re.search(r'[（(]([^)）]+)[)）]', name)
            if match:
                code = match.group(1).strip()
            else:
                # 使用时间戳生成唯一编码
                import random
                code = f"DDG-{datetime.now().strftime('%Y%m%d%H%M%S%f')}-{random.randint(1000, 9999)}"

        # 如果没有名称，使用编码作为名称
        if not name and code:
            name = f"驱逐舰-{code}"
        elif not name and not code:
            # 如果都没有，生成一个默认名称
            name = f"未知驱逐舰-{datetime.now().strftime('%Y%m%d%H%M%S')}"
            code = f"DDG-{datetime.now().strftime('%Y%m%d%H%M%S')}"

        # 解析各类字段
        displacement = parse_float_field(destroyer_data.get('排水量'))
        max_speed = parse_float_field(destroyer_data.get('最高速度'))
        endurance = parse_float_field(destroyer_data.get('最大里程'))
        construction_year = parse_year_field(destroyer_data.get('生产日期'))

        # 动力系统
        propulsion_type = destroyer_data.get('动力系统') or ''

        # 武器类型 - 转为结构化数据
        armament_str = destroyer_data.get('武器类型') or ''
        armament = {
            'description': armament_str,
            'systems': parse_list_field(armament_str, '，')
        }

        # 雷达类型 - 转为列表
        sensors = parse_list_field(destroyer_data.get('雷达类型') or '')

        # 服役时间
        service_start_time = destroyer_data.get('服役时间') or ''

        # 构建态势对象数据
        situation_data = {
            'object_type': ObjectType.SEA_TARGET,  # 海上目标
            'name': name,
            'code': code,
            'model': name,  # 使用名称作为型号
            'organization': '未指定',  # 可从数据中推断
            'deployment_status': DeploymentStatus.DEPLOYED,

            # 类型特定数据
            'type_specific_data': {
                'target_category': '驱逐舰',  # 直接使用驱逐舰作为类别
                'displacement': displacement,
                'max_speed': max_speed,
                'endurance': endurance,
                'propulsion_type': propulsion_type,
                'armament': armament,
                'sensors': sensors,
                'construction_year': construction_year,
                'service_start_time': service_start_time,
                'production_date': destroyer_data.get('生产日期') or '',
            },

            # 适用场景
            'applicable_scenarios': ['海上作战', '反潜作战', '防空作战', '护航'],

            # 标签
            'tags': ['驱逐舰', '军舰', '海上目标'],

            # 其他元数据
            'extra_metadata': {
                'data_source': 'excel_import',
                'import_time': datetime.now().isoformat(),
                'original_data': str(destroyer_data)
            },

            # 审计信息
            'created_by': 'destroyer_import_script',
            'updated_by': 'destroyer_import_script'
        }

        logger.debug(f"成功转换驱逐舰数据: {name} (编码: {code})")
        return situation_data

    except Exception as e:
        logger.error(f"转换驱逐舰数据失败: {e}")
        logger.error(f"原始数据: {destroyer_data}")
        raise


async def import_destroyer_data(excel_file_path: str, batch_size: int = 10):
    """
    导入驱逐舰数据到数据库

    Args:
        excel_file_path: Excel 文件路径
        batch_size: 批量处理大小
    """
    logger.info(f"=" * 80)
    logger.info(f"开始导入驱逐舰数据")
    logger.info(f"文件路径: {excel_file_path}")
    logger.info(f"=" * 80)

    try:
        # 读取 Excel 文件
        destroyer_data_list = read_excel_file(excel_file_path)
        logger.info(f"成功读取 Excel 文件，共 {len(destroyer_data_list)} 条驱逐舰数据")

        # 首先初始化数据库（确保表结构正确）
        logger.info("正在初始化数据库...")
        from src.situation.database import init_db
        await init_db()
        logger.info("数据库初始化完成")

        # 创建数据库会话
        async for db in get_db_session():
            success_count = 0
            error_count = 0
            skipped_count = 0
            error_details = []

            for i, destroyer_data in enumerate(destroyer_data_list, 1):
                try:
                    logger.info(f"\n{'=' * 60}")
                    logger.info(f"处理第 {i}/{len(destroyer_data_list)} 条数据")

                    # 转换为态势对象格式
                    situation_data = convert_to_situation_object(destroyer_data)

                    logger.info(f"驱逐舰名称: {situation_data['name']}")
                    logger.info(f"驱逐舰编码: {situation_data.get('code', '无')}")
                    logger.info(f"排水量: {situation_data['type_specific_data'].get('displacement', '无')} 吨")

                    # 创建态势对象
                    obj_create = SituationObjectCreate(**situation_data)

                    # 创建态势对象 (SituationService.create_object 已经内置了编码重复检查)
                    created_object = await SituationService.create_object(
                        db, obj_create, user="destroyer_import_script"
                    )
                    logger.info(f"✓ 成功创建态势对象: {created_object.name} (ID: {created_object.id})")
                    success_count += 1

                except ValueError as e:
                    error_msg = str(e)
                    if "已存在" in error_msg or "重复" in error_msg:
                        logger.warning(f"✗ 数据已存在，跳过: {error_msg}")
                        skipped_count += 1
                        error_details.append({
                            'index': i,
                            'name': destroyer_data.get('名称', '未知'),
                            'reason': '数据已存在',
                            'error': error_msg
                        })
                    else:
                        logger.error(f"✗ 数据验证错误: {error_msg}")
                        error_count += 1
                        error_details.append({
                            'index': i,
                            'name': destroyer_data.get('名称', '未知'),
                            'reason': '数据验证失败',
                            'error': error_msg
                        })
                except Exception as e:
                    logger.error(f"✗ 处理第 {i} 条数据失败: {e}")
                    error_count += 1
                    error_details.append({
                        'index': i,
                        'name': destroyer_data.get('名称', '未知'),
                        'reason': '处理失败',
                        'error': str(e)
                    })
                    continue

            # 输出导入摘要
            logger.info(f"\n{'=' * 80}")
            logger.info("导入完成！")
            logger.info(f"{'=' * 80}")
            logger.info(f"总计: {len(destroyer_data_list)} 条")
            logger.info(f"成功: {success_count} 条")
            logger.info(f"跳过: {skipped_count} 条（已存在）")
            logger.info(f"失败: {error_count} 条")
            logger.info(f"{'=' * 80}")

            # 如果有错误，输出错误详情
            if error_details:
                logger.info("\n错误详情：")
                for detail in error_details:
                    logger.info(f"  第 {detail['index']} 条 - {detail['name']}: {detail['reason']}")
                    logger.debug(f"    错误信息: {detail['error']}")

            return {
                'total': len(destroyer_data_list),
                'success': success_count,
                'skipped': skipped_count,
                'error': error_count,
                'error_details': error_details
            }

    except Exception as e:
        logger.error(f"导入过程中发生严重错误: {e}")
        import traceback
        logger.error(traceback.format_exc())
        raise


async def main():
    """主函数"""
    # 默认文件路径
    default_file = "test/data-situation/destroyer.xlsx"

    # 检查命令行参数
    if len(sys.argv) > 1:
        excel_file = sys.argv[1]
    else:
        excel_file = default_file

    # 检查文件是否存在
    if not os.path.exists(excel_file):
        logger.error(f"文件不存在: {excel_file}")
        logger.info("用法: python scripts/import_destroyer_data.py [excel_file_path]")
        sys.exit(1)

    try:
        # 确保日志目录存在
        os.makedirs("logs", exist_ok=True)

        # 导入数据
        await import_destroyer_data(excel_file)

        logger.info("驱逐舰数据导入成功完成！")

    except Exception as e:
        logger.error(f"导入失败: {e}")
        sys.exit(1)


def run_main():
    """运行主函数的同步包装器"""
    asyncio.run(main())


if __name__ == "__main__":
    run_main()
